"""
有声读物生成服务
"""

import asyncio
import uuid
from typing import Dict, List, Any, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
import os
import tempfile
import json

from ...core.exceptions import BusinessLogicError, ValidationError
from ...core.logging import get_logger, audit_logger
from ...core.config import settings
from ...services.tts import tts_service, VoiceProfile, VoiceSettings
from ...services.cache import cache_service
from ...models.audiobook import AudioBook, AudioBookChapter, AudioBookStatus
from ...repositories.audiobook import (
    AudioBookRepository,
    AudioBookChapterRepository
)
from .processor import (
    audio_book_processor,
    AudioBookStructure,
    ChapterSegment
)

logger = get_logger(__name__)


class GenerationStatus(Enum):
    """生成状态"""
    QUEUED = "queued"           # 排队中
    PREPROCESSING = "preprocessing"  # 预处理中
    GENERATING = "generating"    # 生成中
    POSTPROCESSING = "postprocessing"  # 后处理中
    COMPLETED = "completed"      # 已完成
    FAILED = "failed"           # 失败
    CANCELLED = "cancelled"      # 已取消


@dataclass
class GenerationRequest:
    """生成请求"""
    title: str
    content: str
    author: Optional[str] = None
    description: Optional[str] = None
    language: str = "zh-CN"
    voice_profile: Optional[VoiceProfile] = None
    voice_settings: Optional[VoiceSettings] = None
    output_format: str = "mp3"
    sample_rate: int = 22050
    bitrate: int = 128
    user_id: str = ""
    chapter_separator: str = "\n\n"
    auto_chapters: bool = True
    max_segment_length: int = 5000
    preferred_segment_length: int = 2000
    preserve_paragraphs: bool = True
    quality_preset: str = "balanced"
    
    # 回调函数
    progress_callback: Optional[Callable[[float, str], None]] = None
    status_callback: Optional[Callable[[GenerationStatus, str], None]] = None
    completion_callback: Optional[Callable[[bool, str, Dict[str, Any]], None]] = None


@dataclass
class GenerationTask:
    """生成任务"""
    id: str
    request: GenerationRequest
    status: GenerationStatus = GenerationStatus.QUEUED
    progress: float = 0.0
    start_time: Optional[datetime] = None
    end_time: Optional[datetime] = None
    error_message: Optional[str] = None
    audiobook_id: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)
    retry_count: int = 0
    max_retries: int = 3


class AudioBookGenerator:
    """有声读物生成器"""
    
    def __init__(self):
        self.tasks: Dict[str, GenerationTask] = {}
        self.workers: List[asyncio.Task] = []
        self.max_concurrent = 3
        self._running = False
        
        # 质量预设
        self.quality_presets = {
            "low": {
                "sample_rate": 16000,
                "bitrate": 64,
                "format": "mp3"
            },
            "balanced": {
                "sample_rate": 22050,
                "bitrate": 128,
                "format": "mp3"
            },
            "high": {
                "sample_rate": 44100,
                "bitrate": 256,
                "format": "mp3"
            },
            "ultra": {
                "sample_rate": 48000,
                "bitrate": 320,
                "format": "flac"
            }
        }
    
    async def start(self):
        """启动生成器"""
        if self._running:
            return
        
        self._running = True
        
        # 启动工作进程
        for i in range(self.max_concurrent):
            worker = asyncio.create_task(self._worker(f"worker-{i}"))
            self.workers.append(worker)
        
        logger.info(f"Audio book generator started with {self.max_concurrent} workers")
    
    async def stop(self):
        """停止生成器"""
        if not self._running:
            return
        
        self._running = False
        
        # 取消所有工作进程
        for worker in self.workers:
            worker.cancel()
        
        # 等待工作进程结束
        await asyncio.gather(*self.workers, return_exceptions=True)
        
        self.workers.clear()
        logger.info("Audio book generator stopped")
    
    async def submit_task(self, request: GenerationRequest) -> str:
        """提交生成任务"""
        task_id = str(uuid.uuid4())
        
        # 创建任务
        task = GenerationTask(id=task_id, request=request)
        self.tasks[task_id] = task
        
        # 记录审计日志
        audit_logger.log_user_action(
            user_id=request.user_id,
            action="submit_audiobook_task",
            resource="generation",
            details={
                "task_id": task_id,
                "title": request.title,
                "content_length": len(request.content)
            }
        )
        
        logger.info(f"Submitted audio book generation task: {task_id}")
        return task_id
    
    async def cancel_task(self, task_id: str, user_id: str) -> bool:
        """取消任务"""
        if task_id not in self.tasks:
            return False
        
        task = self.tasks[task_id]
        
        # 检查权限
        if task.request.user_id != user_id:
            return False
        
        # 只能取消未开始或进行中的任务
        if task.status in [GenerationStatus.QUEUED, GenerationStatus.PREPROCESSING, GenerationStatus.GENERATING]:
            task.status = GenerationStatus.CANCELLED
            task.end_time = datetime.utcnow()
            
            # 调用回调
            if task.request.status_callback:
                try:
                    task.request.status_callback(task.status, "任务已取消")
                except Exception as e:
                    logger.error(f"Status callback error: {e}")
            
            logger.info(f"Cancelled audio book generation task: {task_id}")
            return True
        
        return False
    
    async def get_task_status(self, task_id: str, user_id: str) -> Optional[Dict[str, Any]]:
        """获取任务状态"""
        if task_id not in self.tasks:
            return None
        
        task = self.tasks[task_id]
        
        # 检查权限
        if task.request.user_id != user_id:
            return None
        
        return {
            "id": task_id,
            "status": task.status.value,
            "progress": task.progress,
            "start_time": task.start_time.isoformat() if task.start_time else None,
            "end_time": task.end_time.isoformat() if task.end_time else None,
            "error_message": task.error_message,
            "audiobook_id": task.audiobook_id,
            "metadata": task.metadata
        }
    
    async def _worker(self, worker_name: str):
        """工作进程"""
        logger.info(f"Worker {worker_name} started")
        
        while self._running:
            try:
                # 获取下一个任务
                task = await self._get_next_task()
                if not task:
                    await asyncio.sleep(1)
                    continue
                
                logger.info(f"Worker {worker_name} processing task: {task.id}")
                
                # 处理任务
                await self._process_task(task)
                
                # 清理完成的任务（延迟清理）
                if task.status in [GenerationStatus.COMPLETED, GenerationStatus.FAILED, GenerationStatus.CANCELLED]:
                    await asyncio.sleep(300)  # 5分钟后清理
                    if task.id in self.tasks:
                        del self.tasks[task.id]
                
            except Exception as e:
                logger.error(f"Worker {worker_name} error: {e}")
                await asyncio.sleep(5)
        
        logger.info(f"Worker {worker_name} stopped")
    
    async def _get_next_task(self) -> Optional[GenerationTask]:
        """获取下一个待处理任务"""
        for task in self.tasks.values():
            if task.status == GenerationStatus.QUEUED:
                task.status = GenerationStatus.PREPROCESSING
                task.start_time = datetime.utcnow()
                return task
        return None
    
    async def _process_task(self, task: GenerationTask):
        """处理任务"""
        try:
            # 更新状态回调
            await self._update_status(task, GenerationStatus.PREPROCESSING, "开始预处理")
            
            # 预处理文本
            await self._preprocess_text(task)
            
            # 更新状态回调
            await self._update_status(task, GenerationStatus.GENERATING, "开始生成音频")
            
            # 生成音频
            audiobook = await self._generate_audio(task)
            
            # 更新状态回调
            await self._update_status(task, GenerationStatus.POSTPROCESSING, "开始后处理")
            
            # 后处理
            await self._postprocess_audio(task, audiobook)
            
            # 完成
            task.status = GenerationStatus.COMPLETED
            task.end_time = datetime.utcnow()
            task.audiobook_id = audiobook.id
            task.progress = 1.0
            
            await self._update_status(task, GenerationStatus.COMPLETED, "生成完成")
            
            # 调用完成回调
            if task.request.completion_callback:
                try:
                    task.request.completion_callback(True, "生成完成", task.metadata)
                except Exception as e:
                    logger.error(f"Completion callback error: {e}")
            
            # 记录审计日志
            audit_logger.log_user_action(
                user_id=task.request.user_id,
                action="complete_audiobook_task",
                resource="generation",
                details={
                    "task_id": task.id,
                    "audiobook_id": audiobook.id,
                    "duration": task.end_time - task.start_time
                }
            )
            
        except Exception as e:
            logger.error(f"Task {task.id} failed: {e}")
            
            task.status = GenerationStatus.FAILED
            task.end_time = datetime.utcnow()
            task.error_message = str(e)
            
            await self._update_status(task, GenerationStatus.FAILED, f"生成失败: {str(e)}")
            
            # 重试逻辑
            if task.retry_count < task.max_retries:
                task.retry_count += 1
                task.status = GenerationStatus.QUEUED
                task.start_time = None
                task.end_time = None
                task.error_message = None
                logger.info(f"Retrying task {task.id}, attempt {task.retry_count}")
                return
            
            # 调用失败回调
            if task.request.completion_callback:
                try:
                    task.request.completion_callback(False, str(e), task.metadata)
                except Exception as cb_e:
                    logger.error(f"Completion callback error: {cb_e}")
            
            # 记录审计日志
            audit_logger.log_user_action(
                user_id=task.request.user_id,
                action="fail_audiobook_task",
                resource="generation",
                details={
                    "task_id": task.id,
                    "error": str(e),
                    "retry_count": task.retry_count
                }
            )
    
    async def _preprocess_text(self, task: GenerationTask):
        """预处理文本"""
        request = task.request
        
        # 创建音频片段
        structure = await audio_book_processor.create_segments(
            text=request.content,
            max_segment_length=request.max_segment_length,
            preferred_segment_length=request.preferred_segment_length,
            language=request.language,
            preserve_paragraphs=request.preserve_paragraphs
        )
        
        # 保存结构信息
        task.metadata.update({
            "structure": {
                "title": structure.title,
                "author": structure.author,
                "language": structure.language,
                "total_segments": structure.total_segments,
                "estimated_total_duration": structure.estimated_total_duration,
                "structure_info": structure.structure_info
            }
        })
        
        # 更新进度
        task.progress = 0.1
        await self._update_progress(task, 0.1, "文本预处理完成")
        
        logger.info(f"Preprocessed text for task {task.id}: {structure.total_segments} segments")
    
    async def _generate_audio(self, task: GenerationTask) -> AudioBook:
        """生成音频"""
        request = task.request
        structure_info = task.metadata["structure"]
        
        # 创建Repository
        audiobook_repo = AudioBookRepository()
        chapter_repo = AudioBookChapterRepository()
        
        # 创建有声读物记录
        audiobook_data = {
            "title": request.title,
            "author": request.author,
            "description": request.description,
            "language": request.language,
            "user_id": request.user_id,
            "status": AudioBookStatus.PROCESSING,
            "progress": 0.0
        }
        
        # 应用质量预设
        quality_settings = self.quality_presets.get(request.quality_preset, self.quality_presets["balanced"])
        audiobook_data.update(quality_settings)
        
        audiobook = await audiobook_repo.create_with_attributes(**audiobook_data)
        
        # 获取语音配置
        voice_profile = request.voice_profile
        if not voice_profile:
            # 使用默认语音
            voices = await tts_service.get_available_voices()
            voice_profile = voices[0] if voices else None
        
        # 生成每个片段的音频
        segments = structure_info.get("segments", [])
        total_segments = len(segments)
        
        for i, segment in enumerate(segments):
            if task.status == GenerationStatus.CANCELLED:
                raise BusinessLogicError("Task was cancelled")
            
            try:
                # 生成音频
                audio_data = await tts_service.synthesize(
                    text=segment.content,
                    voice_profile=voice_profile,
                    voice_settings=request.voice_settings,
                    output_format=request.output_format
                )
                
                if not audio_data.success:
                    raise BusinessLogicError(f"TTS failed: {audio_data.error_message}")
                
                # 保存音频文件
                file_path = await self._save_audio_segment(
                    audiobook.id,
                    segment.order,
                    audio_data.audio_data,
                    request.output_format
                )
                
                # 创建章节记录
                chapter_data = {
                    "audiobook_id": audiobook.id,
                    "title": segment.title,
                    "order": segment.order,
                    "content": segment.content,
                    "duration": segment.estimated_duration,
                    "file_path": file_path,
                    "file_size": len(audio_data.audio_data),
                    "voice_id": voice_profile.id if voice_profile else None,
                    "status": AudioBookStatus.COMPLETED
                }
                
                await chapter_repo.create_with_attributes(**chapter_data)
                
                # 更新进度
                progress = 0.1 + (0.7 * (i + 1) / total_segments)
                task.progress = progress
                await self._update_progress(task, progress, f"生成音频片段 {i+1}/{total_segments}")
                
            except Exception as e:
                logger.error(f"Failed to generate segment {i}: {e}")
                raise
        
        logger.info(f"Generated audio for task {task.id}, audiobook {audiobook.id}")
        return audiobook
    
    async def _postprocess_audio(self, task: GenerationTask, audiobook: AudioBook):
        """后处理音频"""
        # 合并所有音频片段
        chapter_repo = AudioBookChapterRepository()
        chapters = await chapter_repo.get_by_audiobook(audiobook.id)
        
        if len(chapters) > 1:
            # 合并音频文件
            merged_file_path = await self._merge_audio_files(
                audiobook.id,
                [ch.file_path for ch in chapters if ch.file_path],
                task.request.output_format
            )
            
            # 更新有声读物信息
            audiobook.file_path = merged_file_path
        
        # 计算总时长
        total_duration = sum(ch.duration for ch in chapters if ch.duration)
        
        # 更新有声读物状态
        audiobook_repo.update_status(audiobook.id, AudioBookStatus.COMPLETED, 1.0)
        
        # 更新元数据
        audiobook.total_duration = total_duration
        audiobook.chapters_count = len(chapters)
        await audiobook_repo.update(audiobook, {})
        
        # 更新进度
        task.progress = 0.95
        await self._update_progress(task, 0.95, "音频后处理完成")
        
        # 缓存结果
        cache_key = f"audiobook:{audiobook.id}"
        await cache_service.set(cache_key, audiobook.to_dict(), expire=3600)
        
        logger.info(f"Postprocessed audio for audiobook {audiobook.id}")
    
    async def _save_audio_segment(
        self,
        audiobook_id: str,
        segment_order: int,
        audio_data: bytes,
        format: str
    ) -> str:
        """保存音频片段"""
        # 创建目录
        audiobook_dir = os.path.join(settings.DATA_DIR, "audiobooks", audiobook_id)
        os.makedirs(audiobook_dir, exist_ok=True)
        
        # 生成文件名
        filename = f"segment_{segment_order:04d}.{format}"
        file_path = os.path.join(audiobook_dir, filename)
        
        # 保存文件
        with open(file_path, "wb") as f:
            f.write(audio_data)
        
        return file_path
    
    async def _merge_audio_files(
        self,
        audiobook_id: str,
        input_files: List[str],
        output_format: str
    ) -> str:
        """合并音频文件"""
        try:
            from pydub import AudioSegment
            
            # 加载第一个文件
            merged = AudioSegment.from_file(input_files[0])
            
            # 合并剩余文件
            for file_path in input_files[1:]:
                try:
                    audio = AudioSegment.from_file(file_path)
                    merged += audio
                except Exception as e:
                    logger.error(f"Failed to load audio file {file_path}: {e}")
                    continue
            
            # 保存合并后的文件
            audiobook_dir = os.path.join(settings.DATA_DIR, "audiobooks", audiobook_id)
            output_filename = f"complete.{output_format}"
            output_path = os.path.join(audiobook_dir, output_filename)
            
            # 导出文件
            if output_format == "mp3":
                merged.export(output_path, format="mp3", bitrate="128k")
            elif output_format == "wav":
                merged.export(output_path, format="wav")
            elif output_format == "flac":
                merged.export(output_path, format="flac")
            
            return output_path
            
        except ImportError:
            logger.warning("pydub not available, skipping audio merge")
            # 返回第一个文件作为替代
            return input_files[0] if input_files else ""
        except Exception as e:
            logger.error(f"Failed to merge audio files: {e}")
            # 返回第一个文件作为替代
            return input_files[0] if input_files else ""
    
    async def _update_status(self, task: GenerationTask, status: GenerationStatus, message: str):
        """更新任务状态"""
        old_status = task.status
        task.status = status
        
        # 调用状态回调
        if task.request.status_callback:
            try:
                task.request.status_callback(status, message)
            except Exception as e:
                logger.error(f"Status callback error: {e}")
        
        logger.debug(f"Task {task.id} status: {old_status.value} -> {status.value}")
    
    async def _update_progress(self, task: GenerationTask, progress: float, message: str):
        """更新任务进度"""
        task.progress = progress
        
        # 调用进度回调
        if task.request.progress_callback:
            try:
                task.request.progress_callback(progress, message)
            except Exception as e:
                logger.error(f"Progress callback error: {e}")
    
    def get_worker_stats(self) -> Dict[str, Any]:
        """获取工作进程统计"""
        return {
            "running": self._running,
            "max_concurrent": self.max_concurrent,
            "active_workers": len(self.workers),
            "total_tasks": len(self.tasks),
            "queued_tasks": len([t for t in self.tasks.values() if t.status == GenerationStatus.QUEUED]),
            "processing_tasks": len([t for t in self.tasks.values() if t.status in [
                GenerationStatus.PREPROCESSING, GenerationStatus.GENERATING, GenerationStatus.POSTPROCESSING
            ]]),
            "completed_tasks": len([t for t in self.tasks.values() if t.status == GenerationStatus.COMPLETED]),
            "failed_tasks": len([t for t in self.tasks.values() if t.status == GenerationStatus.FAILED])
        }


# 全局生成器实例
audio_book_generator = AudioBookGenerator()
